package com.openatc.agent.service.arterial;

import algorithm.Greenwave;
import algorithm.Kdalgorithm;
import com.openatc.agent.service.arterial.exception.IntersectionCycleException;
import com.openatc.model.model.Pattern;
import com.openatc.model.model.Split;
import com.openatc.optimize.fixedtimeplan.config.cross.PhaseOpt;
import com.openatc.optimize.fixedtimeplan.enums.OptTypeEnum;
import model.RouteIntsection;
import model.RouteOpt;
import model.RoutePara;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.LinkedList;
import java.util.List;

/**
 * @author ：panleilei
 * @description
 * @modified By：
 * @version:
 * @date ：Created in 2024/1/23 16:36
 */
@Component
public class DynamicGreenStrategy extends AbstractArterialTrafficControl {
    private Kdalgorithm kdalgorithm = new Kdalgorithm();
    @Override
    public String strategy() {
        return ArterialTrafficControlStrategies.DYNAMICGREEN.getStrategy();
    }

    /**
     * todo  暂时使用绿波协调的方法，后续实现动态绿波
     * @param routePara
     * @return
     */
    @Override
    public RouteOpt optimize(RoutePara routePara) {
        //计算优化
        double intslenth[] = new double[routePara.getDevs().size()]; //保存路口长度的值
        double intslenthup[] = new double[routePara.getDevs().size()]; //保存上行路口长度的值
        double intslenthdown[] = new double[routePara.getDevs().size()]; //保存下行路口长度的值

        int intssplit[] = new int[routePara.getDevs().size()]; //保存路口协调相位绿信比的值

        int intsoffset[] = new int[routePara.getDevs().size()]; //保存优化后各路口的相位差的值
        int intsabs[] = new int[routePara.getDevs().size()];//各路口协调相位所属周期时间位置，用来计算绝对相位差
        int intscycle[] = new int[routePara.getDevs().size()];  // 保存各路口周期长度
        double intsvelup = 0.00;
        double intsveldown = 0.00;

        intsvelup = routePara.getUpspeed();
        intsveldown = routePara.getDownspeed();

        List<RouteIntsection> deviceList = routePara.getDevs();

        String keyAgentid = routePara.getKeyintsid();
        // 得到关键路口的周期
        int keyCycle = calKeyIntersectionCycle(keyAgentid, deviceList);
        if (keyCycle == 0)
            throw new IntersectionCycleException(keyAgentid);

        // 优化各路口方案
        int phaseno = 0;
        int lastIntsWidth = 0;
        for (RouteIntsection device : deviceList) {
            Pattern pattern = device.getFeature().getPatternList().get(0);
            String fixedtimeplantype = device.getFixedtimeplantype();
            if (!StringUtils.hasLength(fixedtimeplantype)) {
                optType = OptTypeEnum.CYCLE_OPT;
            } else {
                optType = OptTypeEnum.getOptTypeEnumByValue(fixedtimeplantype);
            }
            List<PhaseOpt> phases = device.getPhases();
            // 优化各路口周期
            cycleopt(keyCycle, device.getFeature(), phases);
            // 保存各路口周期
            intscycle[device.getSortid() - 1] = device.getFeature().getPatternList().get(0).getCycle();
            // 距离默认为路口中心点之间的距离；如果设置了路口宽度，则距离应该是路口之间的路段距离
            //得到默认距离
            intslenth[device.getSortid() - 1] = device.getDistance();
            // 得到上行距离
            intslenthup[device.getSortid() - 1] = device.getDistance() + lastIntsWidth;
            // 得到下行距离
            intslenthdown[device.getSortid() - 1] = device.getDistance() + device.getWidth();

            // 保存上一个路口的宽度，用于计算上行时下个路口的距离
            lastIntsWidth = device.getWidth();

            //得到周期
            int cycle = device.getFeature().getPatternList().get(0).getCycle();
            if (cycle == 0)
                throw new IntersectionCycleException(device.getAgentid());
            //根据相位号得到协调相位值
            if (routePara.getDirection().equals("up"))
                phaseno = device.getForwardphaseid();
            else if (routePara.getDirection().equals("down"))
                phaseno = device.getBackphaseid();
            else
                phaseno = device.getForwardphaseid();

            List<List<Split>> rings = device.getFeature().getPatternList().get(0).getRings();

            label:
            for (int i = 0; i < rings.size(); i++) {
                List<Split> ring = rings.get(i);
                for (Split split : ring) {
                    if (split.getId() == phaseno) {
                        intssplit[device.getSortid() - 1] = split.getValue();
                        break label;
                    } else {
                        intsabs[device.getSortid() - 1] += split.getValue();
                    }
                }
            }

            while (intsabs[device.getSortid() - 1] >= cycle) {
                intsabs[device.getSortid() - 1] -= cycle;
            }
        }

        boolean allCycleEqual = isAllCycleEqual(intscycle);
        if (!allCycleEqual)
            // todo 填入有问题的路口id
            throw new IntersectionCycleException(keyAgentid);
        kdalgorithm.setAftcycle(keyCycle);
        if (routePara.getDirection().equals("up"))
            intsoffset = kdalgorithm.offsetOpt(routePara.getDirection(), intslenthup, intsvelup, intssplit);
        else if (routePara.getDirection().equals("down"))
            intsoffset = kdalgorithm.offsetOpt(routePara.getDirection(), intslenthdown, intsveldown, intssplit);
        else
            intsoffset = kdalgorithm.offsetByBiDirection(intslenth, intsvelup, intsveldown, intssplit);

        RouteOpt routeOpt = new RouteOpt();
        routeOpt.setDevs(routePara.getDevs());
        List<RouteIntsection> routeOptList = routeOpt.getDevs();

        for (RouteIntsection device : routeOptList) {

            //把每个路口相位差转成绝对相位差，start位置也会变化
            intsoffset[device.getSortid() - 1] -= intsabs[device.getSortid() - 1];
            if (intsoffset[device.getSortid() - 1] < 0)
                intsoffset[device.getSortid() - 1] += keyCycle;

            device.getFeature().getPatternList().get(0).setOffset(intsoffset[device.getSortid() - 1]);
        }

        // 计算绿波带宽
        routeOpt.setGreenwave(getBandWidth(null,intsvelup, intsveldown, routeOptList ));
        return routeOpt;

    }


    /**
     * 判断周期长度是否一致
     * @param intscycle
     * @return
     */
    private boolean isAllCycleEqual(int[] intscycle) {
        if (intscycle.length == 0){
            return false;
        }
        int firstElement = intscycle[0];
        for (int i : intscycle){
            if (firstElement != i){
                return false;
            }
        }
        return true;
    }

    /**
     * 获取干线协调带宽
     * @param type 计算带宽类型，green - 绿波带宽， red - 红波带宽
     * @param intsvelup 上行速度
     * @param intsveldown 下行速度
     * @param devs 路口参数
     * @return
     */
    public List<Greenwave> getBandWidth(String type, double intsvelup, double intsveldown, List<RouteIntsection> devs){

        // todo: 完善红波带宽计算
        type = "green";

        int intsoffset[] = new int[devs.size()]; // 保存优化后的相位差
        int intssplitup[] = new int[devs.size()]; // 保存路口协调相位绿信比的值
        int intssplitdown[] = new int[devs.size()]; // 保存路口协调相位绿信比的值

        int intscycle[] = new int[devs.size()]; // 保存协调路口方案的周期
//        int intsdistances[] = new int[devs.size()]; // 保存各路口的距离

        // 初始化各路口协调相位绿信比
        for (RouteIntsection dev : devs) {
            // 保存周期
            intscycle[dev.getSortid() - 1] = dev.getFeature().getPatternList().get(0).getCycle();
            // 保存各路口相位差
            intsoffset[dev.getSortid() - 1] = dev.getFeature().getPatternList().get(0).getOffset();
            // 保存各路口距离
//            intsdistances[dev.getSortid() - 1] = dev.getDistance();
            // 协调相位号
            int phasenoUp;
            phasenoUp = dev.getForwardphaseid();
            int phasenoDown;
            phasenoDown = dev.getBackphaseid();

            List<List<Split>> rings = dev.getFeature().getPatternList().get(0).getRings();
            // 获取参与协调相位的绿信比
            label:
            for (int i = 0; i < rings.size(); i++) {
                for (Split split : rings.get(i)) {
                    if (phasenoUp == split.getId()) {
                        intssplitup[dev.getSortid() - 1] = split.getValue();
                        break label;
                    }
                }
            }
            label:
            for (int i = 0; i < rings.size(); i++) {
                for (Split split : rings.get(i)) {
                    if (phasenoDown == split.getId()) {
                        intssplitdown[dev.getSortid() - 1] = split.getValue();
                        break label;
                    }
                }
            }
        }

        kdalgorithm.calbandwidth("up",intsvelup, intssplitup,intsoffset );
        kdalgorithm.calbandwidth("down",intsveldown, intssplitdown,intsoffset );

        List<Greenwave> greenwaveList = new LinkedList<>();
        greenwaveList.add(kdalgorithm.getGwup());
        greenwaveList.add(kdalgorithm.getGwdown());

        return greenwaveList;
    }

}
